#include "Planet.hh"
#include "Planet/BuildProducts.hh"
#include <algorithm>
#include <boost/nondet_random.hpp>
#include <boost/random.hpp>
#include <cmath>
#include "../Exception.hh"

using namespace boost;
using namespace std;
using namespace World;

spPlanet Planet::construct(u1 id, spStar star) {
    if (!star)
        throw NullPointerException("star");

    return spPlanet(new Planet(id, star));
}

Planet::Planet(u1 id, spStar star):
    lock(QMutex::Recursive),
    id(id),
    star(star),
    owner(),
    size(),
    population(),
    food(),
    dwelling(),
    factory(),
    farm(),
    reactor(),
    fighter(),
    tank()
{
    random_device rng("/dev/urandom");
    uniform_int< u8 > range(1000, u8(10) * u8(1000000000));
    variate_generator< random_device&, uniform_int< u8 > > random(rng, range);
    size = random();
}

void Planet::next_turn() {
    QMutexLocker locker(&lock);
    spPlayer _owner = owner.toStrongRef();

    if (_owner.isNull())
        return;

    u8 _food = food + food_production() - population;
    f8 popGrowth = min(dwelling_rate(), food_rate());
    popGrowth = max(0.8, popGrowth); // Max az emberek 20%-a halhat meg.
    popGrowth = min(1.1, popGrowth); // Max 10% lehet a növekedés.
    u8 _population = round(popGrowth * population);
    food = _food;
    population = _population;

    if (buildQueue.empty())
        BuildProducts(1000000).execute(*this);
    else if (buildQueue.first()->execute(*this))
        buildQueue.pop_front();
}

bool Planet::build_colony(spPlayer builder) {
    if (builder.isNull())
        throw NullPointerException("Planet::build_colony");

    QMutexLocker locker(&lock);

    if (owner.toStrongRef())
        return false;

    owner      = builder;
    population = 5000;
    dwelling   = 75;
    factory    = 1;
    farm       = 15;
    reactor    = 1;
    return true;
}

bool Planet::attack(spFleet attacker) {
    QMutexLocker locker(&lock);

    if (owner.toStrongRef().isNull())
        return false;

    u8 attackingFighter = attacker->get_fighter();
    u8 attackingTank = attacker->get_tank();
    if (fighter + tank >= attackingFighter + attackingTank) {
        // Nyert a védő
        attacker->add_fighter(-attackingFighter);
        attacker->add_tank(-attackingTank);
        return false;
    }

    // Nyert a támadó
    fighter = 0;
    tank = 0;
    owner = attacker->get_owner();
    return true;
}

/// \brief Egy kör alatt megtermelt élelem.
u8 Planet::food_production() const {
    QMutexLocker locker(&lock);
    f8 workerRate = worker_rate();
    return round(
      500 * min(f8(1), min(energy_rate(), workerRate)) * farm +
      max(0.1 * (workerRate - 1) * population, f8(0))
    );
}

/// \brief Egy kör alatt elvégezhető ipari munka (építkezés, gyártás).
u8 Planet::industry_production() const {
    QMutexLocker locker(&lock);
    f8 workerRate = worker_rate();
    return round(
      10 * min(f8(1), min(energy_rate(), workerRate)) * factory +
      max(0.01 * (workerRate - 1) * population, f8(0))
    );
}

/// \brief Hányszor annyi lakóépület van, mint amennyi kell.
f8 Planet::dwelling_rate() const {
    QMutexLocker locker(&lock);
    f8 available = 100 * min(f8(1), energy_rate()) * dwelling;
    f8 required = population;
    return available / required;
}

/// \brief Hányszor annyi energia van, mint amennyi kell.
f8 Planet::energy_rate() const {
    QMutexLocker locker(&lock);
    f8 available = 100 * min(f8(1), worker_rate()) * reactor;
    f8 required = dwelling + 10 * factory + farm;
    return available / required;
}

/// \brief Hányszor annyi élelem van, mint amennyi kell.
f8 Planet::food_rate() const {
    QMutexLocker locker(&lock);
    f8 available = food + food_production();
    f8 required = population;
    return available / required;
}

/// \brief Hányszor annyi munkás van, mint amennyi kell.
f8 Planet::worker_rate() const {
    QMutexLocker locker(&lock);
    double available = population;
    double required = 100 * (factory + reactor) + 50 * farm + dwelling;
    return available / required;
}

SYNC_WP_GETTER(Planet, Star,   star)
SYNC_WP_GETTER(Planet, Player, owner)

QVector< Planet::spTask > Planet::get_tasks() const {
    return buildQueue;
}

SYNC_GETTER   (Planet, u8,     size)
SYNC_GETTER   (Planet, u8,     population)
SYNC_GETTER   (Planet, u8,     food)
SYNC_GETTER   (Planet, u8,     dwelling)
SYNC_GETTER   (Planet, u8,     factory)
SYNC_GETTER   (Planet, u8,     farm)
SYNC_GETTER   (Planet, u8,     reactor)
SYNC_GETTER   (Planet, u8,     fighter)
SYNC_GETTER   (Planet, u8,     tank)
SYNC_ADDER    (Planet, i8,     dwelling)
SYNC_ADDER    (Planet, i8,     factory)
SYNC_ADDER    (Planet, i8,     farm)
SYNC_ADDER    (Planet, i8,     reactor)
SYNC_ADDER    (Planet, i8,     fighter)
SYNC_ADDER    (Planet, i8,     tank)

void Planet::add_task(Task* task) {
    add_task(spTask(task));
}

void Planet::add_task(spTask task) {
    QMutexLocker locker(&lock);
    buildQueue.append(task);
}

void Planet::del_task(u8 idx) {
    QMutexLocker locker(&lock);
    buildQueue.remove(idx);
}
